/**
 * 判断child是否是parent本身或者其子元素
 * @param {*} parent 父 dom元素
 * @param {*} child 子 dom元素
 */
 export function isChildElement(parent, child) {
    if (!parent && !child) {
        throw new Error('缺少参数');
    }

    if (parent === child) {
        return true;
    }

    const { childNodes = [] } = parent || {};

    for (let c = 0; c < childNodes.length; c++) {
        const childNode = childNodes[c];

        if (childNode === child) {
            return true;
        } else {
            const exist = isChildElement(childNode, child);

            if (exist) {
                return true;
            }
        }
    }

    return false;
}

/**
 * 光标移至最后
 * @param {*} dom 元素
 */
export function cursorMoveToEnd(dom) {
    //解决ff不获取焦点无法定位问题
    dom.focus();
    const range = window.getSelection();
    //range 选择current下所有子内容
    range.selectAllChildren(dom);
    //光标移至最后
    range.collapseToEnd();
}

/**
 * 夹逼函数，将value限制在min和max之间
 * @param {*} value
 * @param {*} min 最小值
 * @param {*} max 最大值
 */
export function clamp(value, min, max) {
    return Math.min(Math.max(min, max), Math.max(value, min));
}

/**
 * 获取同级兄弟元素
 * @param {*} elem 当前元素
 */
export function siblingElems(elem) {
    const nodes = [];
    const _elem = elem;

    while ((elem = elem.previousSibling)) {
        if (elem.nodeType === 1) {
            nodes.push(elem);
        }
    }

    elem = _elem;

    while ((elem = elem.nextSibling)) {
        if (elem.nodeType === 1) {
            nodes.push(elem);
        }
    }

    return nodes;
}

/**
 * 生成uuid
 */
export function generateUUID() {
    let d = new Date().getTime();

    if (window.performance && typeof window.performance.now === 'function') {
        d += performance.now();
    }

    const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        const r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16);
    });

    return uuid;
}

/**
 * 判断点击位置的e.target是否在wrap里边
 * @param {*} wrap document.querySelector("#switch_span_btn")  或者是 Ref.current
 * @param {*} target e.target
 */
export function isInWrap(wrap, target) {
    if (!wrap) {
        return false;
    }
    if (wrap === target) {
        return true;
    }
    if (wrap.children && wrap.children.length) {
        let isContain = false;
        for (let child of [...wrap.children]) {
            if (isInWrap(child, target)) {
                isContain = true;
                break;
            }
        }

        return isContain;
    } else {
        return false;
    }
}

/**
 * 去除首尾空格
 * @param {*} str 目标字符串
 */
export function trim(str) {
    str = str.replace(/^\s*|\s*$/, '');
    return str;
}

/**
 * 字节转换
 * @param {*} limit
 */
export function changeBytes(limit) {
    if (!limit) {
        return '';
    }
    var size = '';
    if (limit < 0.1 * 1024) {
        //小于0.1KB，则转化成B
        size = limit.toFixed(2) + 'B';
    } else if (limit < 0.1 * 1024 * 1024) {
        //小于0.1MB，则转化成KB
        size = (limit / 1024).toFixed(2) + 'KB';
    } else if (limit < 0.1 * 1024 * 1024 * 1024) {
        //小于0.1GB，则转化成MB
        size = (limit / (1024 * 1024)).toFixed(2) + 'MB';
    } else {
        //其他转化成GB
        size = (limit / (1024 * 1024 * 1024)).toFixed(2) + 'GB';
    }

    var sizeStr = size + ''; //转成字符串
    var index = sizeStr.indexOf('.'); //获取小数点处的索引
    var dou = sizeStr.substr(index + 1, 2); //获取小数点后两位的值
    if (dou == '00') {
        //判断后两位是否为00，如果是则删除00
        return sizeStr.substring(0, index) + sizeStr.substr(index + 3, 2);
    }
    return size;
}

//日期格式化
/**
 * 对Date的扩展，将 Date 转化为指定格式的String
 * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q) 可以用 1-2 个占位符
 * 年(y)可以用 1-4 个占位符，毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
 * eg:
 * dateFormat(new Date(),"yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423
 * dateFormat(new Date(),"yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04
 * dateFormat(new Date(),"yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04
 * dateFormat(new Date(),"yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04
 * dateFormat(new Date(),"yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
 */
export function dateFormat(date, format) {
    if (!date) {
        return;
    }

    var iDate = null;
    if (typeof date === 'number') {
        iDate = new Date(date);
    } else if (typeof date === 'string') {
        date = date.replace(/-/g, '/');
        if (date.indexOf('T') > -1) {
            let dotIndex = date.indexOf('.');
            date = date.substr(0, dotIndex);
            date = date.replace('T', ' ');
            date = new Date(date).getTime();
        }

        if (!isNaN(Number(date))) {
            date = Number(date);
        }

        iDate = new Date(date);
    } else if (date instanceof Date) {
        iDate = date;
    } else {
        return false;
    }

    var o = {
        'M+': iDate.getMonth() + 1, //月份
        'd+': iDate.getDate(), //日
        'h+': iDate.getHours() % 24 === 0 ? 24 : iDate.getHours() % 24, //小时
        'H+': iDate.getHours(), //小时
        'm+': iDate.getMinutes(), //分
        's+': iDate.getSeconds(), //秒
        'q+': Math.floor((iDate.getMonth() + 3) / 3), //季度
        S: iDate.getMilliseconds(), //毫秒
    };

    if (!format) {
        format = 'yyyy-MM-dd hh:mm:ss';
    }
    var week = {
        0: '\u65e5',
        1: '\u4e00',
        2: '\u4e8c',
        3: '\u4e09',
        4: '\u56db',
        5: '\u4e94',
        6: '\u516d',
    };
    if (/(y+)/.test(format)) {
        format = format.replace(RegExp.$1, (iDate.getFullYear() + '').substr(4 - RegExp.$1.length));
    }
    if (/(E+)/.test(format)) {
        format = format.replace(
            RegExp.$1,
            (RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '\u661f\u671f' : '\u5468') : '') +
                week[iDate.getDay() + '']
        );
    }
    for (var k in o) {
        if (new RegExp('(' + k + ')').test(format)) {
            format = format.replace(
                RegExp.$1,
                RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
            );
        }
    }

    return format;
}

/**
 * wkt 转 json
 * @param {*} wkt
 */
// export function wktToJson(wkt) {
//     if (!wkt) {
//         return;
//     }

//     let type = wkt.match(/[a-zA-Z]+/g);
//     if (!type) {
//         return null;
//     }
//     type = type[0].toLowerCase();

//     const matches = wkt.match(/\([^()]*\)/g);
//     if (!matches) {
//         return null;
//     }

//     let lngLats = matches.map((str) => {
//         str = str.replace(/[()]/g, '');
//         return str.split(',').map((lngLat) => {
//             lngLat = lngLat.split(' ');
//             return {
//                 lng: Number(lngLat[0]),
//                 lat: Number(lngLat[1]),
//             };
//         });
//     });

//     // TODO:暂只支持一个面的情况，之后会处理带洞的面
//     if (type.includes('line')) {
//         type = 'line';
//     }
//     if (type.includes('point')) {
//         type = 'point';
//         lngLats = lngLats[0];
//     }
//     if (type.includes('polygon')) {
//         type = 'polygon';
//     }

//     return {
//         type,
//         lngLats: lngLats[0],
//     };
// }

/**
 * 使用test方法实现模糊查询
 * @param  {Array}  list     原数组
 * @param  {String} keyWord  查询的关键词
 * @return {Array}           查询的结果
 */
export function fuzzyQuery(list, keyWord) {
    var arr = [];
    for (var i = 0; i < list.length; i++) {
        if (list[i].name.indexOf(keyWord) >= 0) {
            arr.push(list[i]);
        }

        if (list[i].code.indexOf(keyWord) >= 0) {
            arr.push(list[i]);
        }
    }
    return arr;
}


/**
 * geometry转wkt
 * @param  geometry 要素空间信息
 */
export function geometryToWkt(geometry) {
    let {
        type,
        coordinates
    } = geometry;

    let coordinateWkt = '';
    const newType = type.toLowerCase();

    let typeMap = {
        'point': getPointWkt,
        'linestring': getLineWkt,
        'multilinestring': getMultiLineWkt,
        'polygon': getPolygonWkt,
        'multipolygon': getMultiPolygonWkt
    };

    // 根据类型调取方法
    if (typeMap[newType]) {
        // 点特殊处理
        if (newType === 'point') {
            coordinateWkt = `(${typeMap[newType](coordinates)})`;
        } else {
            coordinateWkt = typeMap[newType](coordinates);
        }
    }

    // 点
    function getPointWkt(coordinates) {
        if (isArr(coordinates)) {
            return `${coordinates[1]} ${coordinates[0]}`;
        }
    }

    // 线
    function getLineWkt(coordinates) {
        let str = '';
        if (isArr(coordinates)) {
            coordinates.forEach(coordinate => {
                const symbol = str ? ',' : '';
                str += `${symbol}${getPointWkt(coordinate)}`;
            });
        }
        return `(${str})`;
    }

    // 多线
    function getMultiLineWkt(coordinates) {
        let str = '';
        if (isArr(coordinates)) {
            coordinates.forEach(coordinate => {
                const symbol = str ? ',' : '';
                str += `${symbol}${getLineWkt(coordinate)}`;
            });
        }
        return `(${str})`;
    }

    // 面(处理方式同多线)
    function getPolygonWkt(coordinates) {
        return getMultiLineWkt(coordinates);
    }

    // 多面
    function getMultiPolygonWkt(coordinates) {
        let str = '';
        if (isArr(coordinates)) {
            coordinates.forEach(coordinate => {
                const symbol = str ? ',' : '';
                str += `${symbol}${getPolygonWkt(coordinate)}`;
            });
        }
        return `(${str})`;
    }

    // 判断是否为数组
    function isArr(arr) {
        return Array.isArray(arr);
    }

    return `${type}${coordinateWkt}`;
}

/**
 * wkt转geometry
 * @param  wkt wkt数据
 */
export function wktToGeometry(wkt) {
    if (!wkt) {
        return;
    }

    let type = wkt.match(/[a-zA-Z]+/g);
    if (!type) {
        return null;
    }

    let coordinates = wkt.replace(type, '');
    coordinates = coordinates.replaceAll('(', '[');
    coordinates = coordinates.replaceAll(')', ']');
    coordinates = coordinates.replaceAll(' ', ',');

    function coordinatesFormat(coordinates) {
        const arr = [];
        if (!isArr) {
            return;
        }
        for (let i = 0; i < coordinates.length; i++) {
            if (isArr(coordinates[i])) {
                coordinates[i] = coordinatesFormat(coordinates[i]);
            } else {
                if (i % 2 === 0) {
                    arr.push([coordinates[i + 1], coordinates[i]]);
                }
                if (i === coordinates.length - 1) {
                    coordinates = arr;
                }
            }
        }
        return coordinates;
    }

    function getType(type) {
        let newType = type.toLowerCase();

        if (newType.includes('point')) {
            return 'point';
        }

        if (newType.includes('line')) {
            return 'line';
        }

        if (newType.includes('polygon')) {
            return 'polygon';
        }

        return newType;
    }

    function isArr(arr) {
        return Array.isArray(arr);
    }

    let lngLats = coordinatesFormat(JSON.parse(coordinates));

    const newType = getType(type[0]);

    // 点特殊处理
    if (newType === 'point') {
        lngLats = lngLats[0];
    }

    return {
        type: newType,
        lngLats
    };
}

//倒序
export function reverseCompare(x, y) {
    if (x < y) {
        return 1;
    } else if (x > y) {
        return -1;
    } else {
        return 0;
    }
}

/**
 * LRU缓存算法
 * @param {*} capacity 缓存容量
 * @param {*} callback 被移除缓存的key值
 * @returns
 */
export function lruCache(capacity, callback) {
    if (typeof capacity != 'number' || capacity < 1) {
        throw new Error('参数capacity必须大于0');
    }

    // 缓存对象
    const cache = {};
    // 记录尾部key
    let tailKey = null;
    // 记录头部key
    let firstKey = null;
    // 记录当前缓存条数
    let count = 0;

    /**
     * 访问某一key数据，并移动该节点到头部
     * 连接该节点的前后节点
     * 该节点作为first节点的后驱节点，并且自己成为first节点
     * @param {string} key
     * @param value
     * @returns
     */
    function move(key, value) {
        // 缓存中是否有该节点
        const oldNode = cache[key];
        if (!oldNode) {
            return false;
        }

        // 前驱和后驱节点
        const { prevKey, nextKey } = oldNode;

        // 后驱节点存在，说明不是first节点，将其移动与头部关联
        if (nextKey) {
            // 前驱节点存在，与后驱节点连接
            if(prevKey) {
                cache[prevKey].nextKey = nextKey;
            } else {
                // 前驱节点不存在，说明需要更新tail节点
                cache[nextKey].prevKey = null;
                tailKey = nextKey;
            }

            // 更新该节点的值，并且该节点成为first节点
            oldNode.prevKey = firstKey;
            oldNode.nextKey = null;
            // 之前的first节点需要指向新的first节点
            cache[firstKey].nextKey = key;
            // 记录最新的头部节点key
            firstKey = key;
        }

        // 后驱节点不存在，说明当前节点是头部节点，不需要移动，只需要更新值
        if (typeof value != 'undefined') {
            oldNode.value = value;
        }

        return true;
    }

    return {
        /**
         * 向缓存中存入值
         * @param {*} key
         * @param {*} value
         * @returns
         */
        set (key, value) {
            // 移动节点成功，说明该节点之前存在，需要更新它的值
            if (move(key, value)){
                return true;
            }

            // 判断容量，超过容量，需要移除最后一个节点，并设置新的尾节点
            if (count == capacity) {
                if (typeof callback == "function") {
                    callback(tailKey);
                }

                const tailNode = cache[tailKey];
                delete cache[tailKey];

                tailKey = tailNode.nextKey;
                if (tailKey) {
                    cache[tailKey].prevKey = null;
                }
            }

            // 该节点为新的节点
            const newNode = {
                value,
                nextKey: null,
                prevKey: null
            }

            if (count == 0) {
                tailKey = key;
            } else {
                cache[firstKey].nextKey = key;
                newNode.prevKey = firstKey;
            }

            cache[key] = newNode;
            firstKey = key;

            if (count < capacity) {
                count++;
            }
        },
        /**
         * 从缓存中取值
         * @param {*} key
         * @returns
         */
        get(key) {
            if (move(key)) {
                return cache[key].value;
            }

            return null;
        },
        getCache() {
            return cache;
        }
    }
}

/**
 * 根据bbox获取瓦片行列号范围
 * @param {*} bbox 
 * @param {*} zoom 
 * @returns 
 */
export function bboxToGrid(bbox, zoom) {
    const x1 = Math.floor((bbox[0] + 180) / (360 / Math.pow(2, zoom)));
    const y1 = Math.ceil((90 - bbox[1]) / (180 / Math.pow(2, zoom - 1)));
    const x2 = Math.ceil((bbox[2] + 180) / (360 / Math.pow(2, zoom)));
    const y2 = Math.floor((90 - bbox[3]) / (180 / Math.pow(2, zoom - 1)));
    return [x1, y1, x2, y2];
}

/**
 * 根据行列号获取经纬度范围
 * @param {*} x 列号
 * @param {*} y 行号
 * @param {*} z 级别
 * @returns 
 */
export function gridToBbox(x,y,z) {
    const x1 = x * 256 * (360 / (Math.pow(2, z) * 256)) - 180;
    const y1 = 90 - y * 256 * (180 / (Math.pow(2, z - 1) * 256));
    const x2 = x1 + 360 / Math.pow(2, z);
    const y2 = y1 + 180 / Math.pow(2, z - 1);
    return [x1, y1, x2, y2];
}

/**
 * 洗牌算法 shuffle
 * @param {*} array 
 * @returns 
 */
export function shuffle(array) {
    const length = array == null ? 0 : array.length
    if (!length) {
        return []
    }
    let index = -1
    const lastIndex = length - 1
    const result = copyArray(array)
    while (++index < length) {
        const rand = index + Math.floor(Math.random() * (lastIndex - index + 1))
        const value = result[rand]
        result[rand] = result[index]
        result[index] = value
    }

    function copyArray(source, array) {
        let index = -1
        const length = source.length

        array || (array = new Array(length))
        while (++index < length) {
            array[index] = source[index]
        }
        return array
    }
    
    return result
}

/**
 * 把数据为数组的用key,key1,key2形式展开成对象
 * @param {*} form 表单数据
 * @returns
 */
export function parseFormValues(form) {
    const formKeys = Object.keys(form);
    formKeys.forEach((key) => {
        const val = form[key];
        if (Array.isArray(val) && val.length > 0) {
            form = { ...form, ...parseSameName(key, val) };
        }
    });
     
    function parseSameName(key, arr) {
        const nameObj = { [key]: arr[0] };
        for (let i = 1; i < arr.length; i++) {
            nameObj[key + i] = arr[i];
        }
        return nameObj;
    }

    return form;
}

/**
 * 获取日期
 * @param {*} d 
 * @returns 
 */
export function formatTime (d) {
    var date = new Date(d)
    const formatNumber = n => {
        n = n.toString()
        return n[1] ? n : '0' + n
    }
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    const hour = date.getHours()
    const minute = date.getMinutes()
    const second = date.getSeconds()
    return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}